package com.odontosis.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.Session;

import com.odontosis.as.OdontosisApplicationService;
import com.odontosis.dao.PagamentoDAO;
import com.odontosis.entidade.Paciente;
import com.odontosis.entidade.Pagamento;
import com.odontosis.entidade.Servico;
import com.odontosis.entidade.StatusServico;
import com.odontosis.entidade.TipoServico;
import com.odontosis.util.DateDiff;

public class PagamentoService extends OdontosisApplicationService<Pagamento> {

	private final PagamentoDAO pagamentoDAO = new PagamentoDAO();
	
	public PagamentoService() throws Exception{
		super(Pagamento.class, new PagamentoDAO());
		//HibernateUtil.closeSession();
	}
	
	public Collection<Pagamento> buscarPagamentosPorPeriodo(Date inicio, Date fim , Paciente paciente){
		return pagamentoDAO.buscarPagamentosPorPeriodo(inicio, fim, paciente, null);
	}
	
	public Collection<Pagamento> buscarPagamentosPorPeriodo(Date inicio, Date fim , Paciente paciente, String nomePaciente){
		return pagamentoDAO.buscarPagamentosPorPeriodo(inicio, fim, paciente, nomePaciente);
	}
	
	public Collection<Pagamento> buscarParcelasIsentasPorPeriodo(Date inicio, Date fim , Paciente paciente, String nomePaciente){
		return pagamentoDAO.buscarParcelasIsentasPorPeriodo(inicio, fim, paciente, nomePaciente);
	}
	
	public Collection<Pagamento> buscarPagamentosPorDataEPaciente(Integer ano,Integer mesInicial, Integer mesFinal, Long pacienteId){
		return pagamentoDAO.buscarPagamentosPorDataEPaciente(ano, mesInicial, mesFinal, pacienteId);
	}
	
	public Collection<Pagamento> buscarPagamentosPorServico(Servico servico){
		return pagamentoDAO.buscarPagamentoPorServico(servico);
	}
	
	public Integer numeroParcelas(Pagamento pagamento){
		return pagamentoDAO.numeroParcelas(pagamento);
	}
	
	public Collection<Pagamento> buscarMaiorVencimento(Paciente paciente){
		//busca os serviços ativos por paciente
		Collection<Servico> listaServicos = pagamentoDAO.buscarServicosAtivos(paciente);
		//grava as ultimas parcelas, uma por serviço
		Collection<Pagamento> parcelas = new ArrayList<Pagamento>();
		for (Servico servico : listaServicos) {
			//se o serviço é manutenção busca a ultima parcela 
			if(servico.getTipoServico() == TipoServico.MANUTENCAO){
				Pagamento pagamento =  pagamentoDAO.buscarUltimaParcela(servico);
				if(pagamento != null){
					parcelas.add(pagamento);
				}
			}
		}
		
		return parcelas;
	}
	
	public boolean isUltimaParcelaVencida(Pagamento pagamento) {
		Pagamento ultimoPagamento = pagamentoDAO.buscarUltimaParcelaNaoPaga(pagamento.getServico(), pagamento.getAnoVencimento(), pagamento.getMesVencimento());
		if(ultimoPagamento != null) {
			return false;
		}
		return true;
	}
	
	private boolean verificaGeracao(Servico servico, int proximoMes, int proximoAno){
		if(servico.isIsento()){
			int mesIsencao = servico.getMesIsencao().intValue();
			int anoIsencao = servico.getAnoIsencao().intValue();
			if((proximoMes  >= mesIsencao && anoIsencao == proximoAno) || proximoAno > anoIsencao){
				return false;
			}
		}
		if(servico.getStatusServico() == StatusServico.FINALIZADO) {
			return false;
		}
		return true;
	}
	
	/**
	 * Gera parcelas para os meses iniciais e uma a mais a partir da data atual
	 * para paciente em mautenção que naum possuirem parcela para 
	 * esses meses, gera três meses a partir da data atual
	 * 
	 * @param paciente
	 */
	public void gerarTaxasManutencao(Paciente paciente){
		Collection<Pagamento> pagamentos = buscarMaiorVencimento(paciente);
		//pagamentos = pagamentoDAO.buscarMaiorVencimento(paciente);
		//converte(pagamentos);
		Calendar dataAtual = Calendar.getInstance();
		Calendar dataUltimaParcela = Calendar.getInstance();
		Calendar prazoParametro = Calendar.getInstance();
		GregorianCalendar gc = new GregorianCalendar();  
		  
		//acrescenta na data final o numero de meses de prestacoes a serem geradas
		gc.add(Calendar.MONTH, buscarNumeroPrestacoesGeradas());
		
		prazoParametro.setTime(new Date(gc.getTimeInMillis()));
		
				
		if(pagamentos != null){
			for (Pagamento pagamento : pagamentos) {
				//if(verificaGeracao(pagamento.getServico())){
					dataUltimaParcela.set(Calendar.MONTH, pagamento.getMesVencimento().intValue()-1);
					dataUltimaParcela.set(pagamento.getAnoVencimento().intValue(), pagamento.getMesVencimento().intValue()-1, 1);

					int diferenca = DateDiff.getDateDiff(Calendar.MONTH, dataUltimaParcela.getTime(), prazoParametro.getTime());

					
					int proximoMes = pagamento.getMesVencimento();
					int proximoAno = pagamento.getAnoVencimento();
					if(diferenca > 0){
						for (; diferenca > 0; diferenca--) {
							proximoMes++;
							if(proximoMes >= 13){
								proximoMes = 1;
								proximoAno++;
							}
							if(!verificaGeracao(pagamento.getServico(),proximoMes,proximoAno )){
								break;
							}
							
							Pagamento pagamentoSalvar = new Pagamento();
							pagamentoSalvar.setAnoVencimento(proximoAno);
							pagamentoSalvar.setId(null);
							pagamentoSalvar.setMesVencimento(proximoMes);
							pagamentoSalvar.setPago(false);
							pagamentoSalvar.setServico(pagamento.getServico());
							pagamentoSalvar.setValor(pagamento.getValor());
							saveOrUpdate(pagamentoSalvar);
						}
					}
					//tira do cache o objeto pagamento, evitou o bug na tela de cadastro de pagamentos
					getSession().refresh(pagamento);
					getSession().evict(pagamento);
			}
		}
	}
	
	public void converte(Collection<Pagamento> pagamentos) {
		ServicoService servicoService = null;
		try{
			 servicoService = new ServicoService();
		}catch (Exception e) {
			e.printStackTrace();
		}
		Collection<Pagamento> antigaCollection = new ArrayList<Pagamento>();
		antigaCollection.addAll(pagamentos);
		pagamentos.clear();
		for (Object object : antigaCollection) {
			Object[] objetos = (Object[]) object; 
			Pagamento pagamento = new Pagamento();
			//id
			pagamento.setId(new Long((Integer) objetos[0]));
			//servico
			pagamento.setServico(servicoService.load(new Long((Integer) objetos[1])));
			//valor
			pagamento.setValor((Double) objetos[2] );
			//mes
			pagamento.setMesVencimento((Integer) objetos[3]);
			//ano
			pagamento.setAnoVencimento((Integer) objetos[4]);
			//pago
			pagamento.setPago((Boolean) objetos[5]);
			pagamentos.add(pagamento); 
		}
	}
	
	public Integer buscarNumeroPrestacoesGeradas(){
		return pagamentoDAO.buscarNumeroPrestacoesGeradas();
	}
	
	public List<Pagamento> buscarPagamentosParaAumento() {
		return pagamentoDAO.buscarPagamentosParaAumento();
	}
	
	public Map<Double,Double> buscarVariaveisAumento() {
		List<Object[]> lista = pagamentoDAO.buscarVariaveisAumento();
		Map<Double,Double> mapa = new HashMap<Double, Double>();
		for (Object[] objects : lista) {
			BigDecimal valorAnterior = (BigDecimal) objects[0];
			BigDecimal valorNovo = (BigDecimal) objects[1];
			mapa.put(valorAnterior.doubleValue(),valorNovo.doubleValue());
		}
		return mapa;
	}
	
	public Session getSession() {
		return pagamentoDAO.getSession(); 
	}
}
